package roborally.model;

import java.util.*;

import roborally.model.filters.ElementFilter;
import roborally.model.interfaces.*;

import be.kuleuven.cs.som.annotate.*;

/**
 * A class representing a board with a width and height consisting of fields created as elements are placed upon them.
 * @Invar	|isValidDimension(getWidth(), getHeight())
 * @Implements Terminatble
 */
public class Board implements Terminatable{
	
	//CONSTRUCTOR
	/**
	 * Constructor of a Board object
	 * @param 	width
	 * @param 	height
	 * @post 	|new.width == width
	 * @post 	|new.height == height
	 * @throws 	IllegalArgumentException
	 *         	Thrown when the given width and height dimension parameters are not valid
	 *         	|!isValidDimension(width,height)
	 */
	public Board(long width, long height) throws IllegalArgumentException {
		if(!isValidDimension(width,height)) throw new IllegalArgumentException("invalid dimensions");
		this.width = width;
		this.height = height;
	}
	
	/**
	 * Returns the width of the Board object.
	 * @return |result == this.width
	 */
	@Basic @Immutable @Raw
	public long getWidth(){ return this.width; }

	/**
	 * Returns the height of the Board object.
	 * @return |result == this.height
	 */
	@Basic @Immutable @Raw
	public long getHeight(){ return this.height; }

	//DIMENSION-related
	/**
	 * This variable represents the width of the board.
	 */
	private final long width;
	/**
	 * This variable represents the height of the board.
	 */
	private final long height;
	
	/**
	 * This variable represents the upper limit for the width for every Board object. (Default: Long.MAX_VALUE)
	 */
	public final static long MAX_WIDTH = Long.MAX_VALUE;
	/**
	 * This variable represents the upper limit for the height of a given Board object. (Default: Long.MAX_VALUE)
	 */
	public final static long MAX_HEIGHT = Long.MAX_VALUE;
	
	//VALIDATORS
	/**
	 * Checks whether the given width and height are valid dimensions for a board.
	 * @param	width
	 * 			The width dimension
	 * @param	height
	 * 			The height dimension
	 * @return	The method returns true when the width is bigger or equal to 0 and smaller than MAX_WIDTH and the height is bigger or equal to 0 and smaller than MAX_HEIGHT
	 * 			|result == (width >= 0 && height >= 0) && ( width < MAX_WIDTH && height < MAX_HEIGHT)
	 */
	public static boolean isValidDimension(long width, long height)
	{	
		return (width >= 0 && height >= 0) && (width < MAX_WIDTH && height < MAX_HEIGHT);
	}
	
	/**
	 * Checks whether the given position is within the bounds of this board.
	 * @param 	pos
	 * 			The Position object for which we want to check if it is valid for this Board object
	 * @return 	The method returns true when both the x coordinate of the Position object is situated
	 *         	between zero (inclusive) and this.getWidth() (exclusive) and the y coordinate is situated between 0 and this.getHeight()
	 *         	| result == (pos.getX() >= 0 && pos.getY() >= 0) && (pos.getX() < this.getWidth() && pos.getY() < this.getHeight())
	 */
	public boolean isValidPosition(Position pos)
	{
		return (pos.getX() >= 0 && pos.getY() >= 0) && (pos.getX() < this.getWidth() && pos.getY() < this.getHeight());
	}
	
	//TERMINATE-related
	/**
	 * Returns whether or not a Board object is terminated.
	 * @return |result == isTerminated
	 */
	@Basic @Raw
	public boolean isTerminated(){ return isTerminated; }
	
	/**
	 * Terminates a Board object.
	 * @effect	|let
	 * 			|	Iterator<Field> iteratorOfFields = getFieldsInUseIterator();
	 * 			|in
	 * 			|	while(iteratorOfFields.hasNext())
	 *			|		iteratorOfFields.next().terminate()
	 * @post 	|new.isTerminated == true
	 * @post 	|new.getFieldsInUse().isEmpty() == true
	 * @note 	When terminated is called on a field object it will remove itself from its associated board
	 */
	@Raw
	public void terminate(){
		if(isTerminated) return;
		//terminate all of the board's fields, we cannot use an iterator because we then a ConcurrentModificationException will be thrown
		for(Field field : getFieldsInUse().values().toArray(new Field[0]))
			field.terminate();
		this.isTerminated = true;
	}

	/**
	 *  A variable that indicates whether or not a Board object is terminated.
	 */
	private boolean isTerminated;
	
	//FIELDS
	
	/**
	 * Returns the HashMap which contains the Field objects which have been instantiated as values
	 * @return |result == fieldsInUse
	 */
	@Basic @Raw
	public Map<Position, Field> getFieldsInUse() { return this.fieldsInUse; }
	
	/**
	 * Returns an iterator over the elements of fieldsInUse.values(), which are Field objects
	 * @return |result == fieldsInUse.values().iterator()
	 */
	@Raw
	public Iterator<Field> getFieldsInUseIterator() {
		return this.fieldsInUse.values().iterator();
	}
	
	/**
	 * HashMap containing the Position objects of the Field objects representing the board as key, and the corresponding Field objects as value
	 */
	private Map<Position, Field> fieldsInUse = new HashMap<Position, Field>();

	//MUTATORS
	/**
	 * Creates a Field object at the given position, adds it to the fieldsInUse Map and returns the new field
	 * @param 	position
	 * @return 	|if(!getFieldsInUse().containsKey(position)) then
	 * 			| result == new Field(this,position)
	 * 			|else
	 * 			| result == getFieldsInUse().get(position)
	 * @post 	|new.getFieldsInUse().containsValue(field) == true
	 * @post 	|new.getFieldsInUse().containsKey(position) == true
	 * @post 	|if(!this.getFieldsInUse().containsKey(position)) then
	 * 			| new.getFieldsInUse().get(position).equals(new Field(this,position)) == true
	 * 			|else
	 * 			| new.getFieldsInUse().get(position).equals(this.getFieldsInUse().get(position)) == true
	 * @throws 	IllegalArgumentException
	 *         	Thrown if the position given as a parameter is not a valid position on the board
	 *         	| !isValidPosition(position)
	 */
	public Field createField(Position position) throws IllegalArgumentException
	{
		if(!isValidPosition(position)) throw new IllegalArgumentException("Position is out of the bounds of the board.");
		if(getFieldsInUse().containsKey(position)) return getFieldsInUse().get(position);
		Field field = new Field(this,position);
		fieldsInUse.put(position, field);
		return field;
	}
	
	/**
	 * Removes the field given as a parameter from getFieldsInUse() and returns true if successful,
	 * returns false if the given field is not associated with this board.
	 * 
	 * @param 	field
	 * @post 	|new.getFieldsInUse().containsKey(field.getPosition()) == false
	 * @post 	|new.getFieldsInUse().containsValue(field) == false
	 * @throws 	IllegalArgumentException
	 * 		   	Thrown when the Field object given as a parameter is null
	 *         	|field == null
	 * @return 	|result == this.getFieldsInUse().containsValue(field)
	 */
	public boolean removeField(Field field) throws IllegalArgumentException
	{
		if(field == null) throw new IllegalArgumentException("Argument 'field' == null");
		if(getFieldsInUse().containsValue(field)){ //if the field is part of this board
			getFieldsInUse().remove(field.getPosition());
			return true;
		}
		return false; //return false if the field is not associated with this board.
	}
	
	/**
	 * A method to merge a board with this one.
	 * @param 	otherBoard
	 * 			The other board to merge with.
	 * @effect 	For each field of the second board that fits into the dimensions of this board and for which there is an effective 
	 * 			corresponding field object on this board, that field and its corresponding one will be merged.
	 * 			|let
	 * 			|	while(otherBoard.getFieldsInUseIterator().hasNext())
	 * 			|		Field otherField = iter.next()
	 * 			|in
	 * 			|	if( this.isValidPosition(otherField.getPosition()) 
	 * 			|		&& this.getFieldsInUse().containsKey(otherField.getPosition()) ) then
	 * 			|			this.getFieldsInUse().get(otherField.getPosition()).merge( otherField )
	 * @effect 	For each field of the second board that fits into the dimensions of this board, but for which there is no effective 
	 * 			corresponding field object on this board, a new field will be created on this board on that position and it will be merged with the other one.
	 * 			|let
	 * 			|	while(otherBoard.getFieldsInUseIterator().hasNext())
	 * 			|		Field otherField = iter.next()
	 * 			|in
	 * 			|	if( this.isValidPosition(otherField.getPosition()) 
	 * 			|		&& !this.getFieldsInUse().containsKey(otherField.getPosition()) ) then
	 * 			|			createField(otherField.getPosition()).merge( otherField )
	 * @effect 	The other board is terminated
	 * 			|otherBoard.terminate()
	 */
	public void merge(Board otherBoard)
	{
		Iterator<Field> iter = otherBoard.getFieldsInUseIterator();
		while(iter.hasNext()) { //iterate over all the field objects of the other board
			Field otherField = iter.next();
			//if there is a corresponding position on this board, go on.
			if(this.isValidPosition(otherField.getPosition()))
			{
				Field thisField;
				if( this.getFieldsInUse().containsKey(otherField.getPosition()) ) //there is a corresponding field object on this position
					thisField = this.getFieldsInUse().get(otherField.getPosition());
				else //else if there is no corresponding field on the first board, then we create a new field on this position on the first board and merge with otherField
					thisField = this.createField(otherField.getPosition());
				//we merge the field on this board with the field on otherBoard that occupy the same position. The other field will also be terminated.
				thisField.merge(otherField);
			}
		}
		//terminate the other board.
		otherBoard.terminate();
	}
	
	/**
	 * Teleports an element to another available random position on the board
	 * @param element
	 * @post	|if(element.isPlacedOnBoard() && element.getField().getBoard() == this) then
	 * 			|	let
	 * 			|	 Random r = new Random()
	 * 			|	 Boolean valid = true
	 * 			|	 Position targetPos;
	 *			|	do {
	 *			|		targetPos = new Position(Math.abs(r.nextLong() % (this.getWidth()-1)), Math.abs(r.nextLong() % (this.getHeight()-1)));
	 *			|		if(getFieldsInUse().containsKey(targetPos)){
	 *			|			valid = getFieldsInUse().get(targetPos).canPutElement(element);
	 *			|	} while (!valid)
	 *			|	in
	 *			|	 new.getFieldsInUse().get(targetPos).getElements().contains(element)
	 */
	public void teleportElement(Element element) {
		if(!(element.isPlacedOnBoard() && element.getField().getBoard() == this)) return; //the element must be placed on this board.
		element.removeFromBoard(); //We remove the element from the board to place it on another position (this guarantees there is at least one open field for the element to be teleported to, namely its original field) 
		Random r = new Random();
		Boolean valid = true;
		Position targetPos;
		do {
			targetPos = new Position(Math.abs(r.nextLong() % (this.getWidth()-1)), Math.abs(r.nextLong() % (this.getHeight()-1)));
			if(getFieldsInUse().containsKey(targetPos)){
				valid = getFieldsInUse().get(targetPos).canPutElement(element);
			}
				
		} while (!valid);
		putElement(targetPos, element);
	}
	
	//PUT
	/**
	 * Puts the element object, which is given as a parameter, at the given position. If there is no Field object at
	 * this position a new Field object will be created. Otherwise an attempt is made to put the element on the existing
	 * field object.
	 * @param position
	 * @param element
	 * @post 	If there is no field object on the board for this position yet, we are sure the element is successfully put on that position. (hence a new field object is created)
	 * 			|if (!this.getFieldsInUse().containsKey(position)) then new.element.getField().getPosition.equals(position) == true
	 * @post 	|new.getFieldsInUse().containsKey(position) == true
	 * @post 	If there already was a field object on the board for this position, then under the condition that we can put an element on it, the element will be successfully put on that field.
	 * 			|if (this.getFieldsInUse().containsKey(position)) then 
	 * 			|	if (this.getFieldsInUse().get(position).canPutElement()) then 
	 * 			|		new.getFieldsInUse().get(position).getElements().contains(element) == true
	 * @throws IllegalArgumentException
	 * 		   Thrown when the given position is not valid for the Board
	 * 		   | !isValidPosition(position)
	 */
	public void putElement(Position position, Element element) throws IllegalArgumentException { 
		if(!isValidPosition(position)) throw new IllegalArgumentException("Position is out of the bounds of the board.");
		//we check if there already is a field object on this position
		Field field = fieldsInUse.get(position);
		if(field == null){ //no field object created on this position yet.
			createField(position).putElement(element); //create the new field and put the element on it
			return;}
		//otherwise (there already is a field object for this position on the board) we put the element on this position, 
		//an IllegalStateException will be thrown when the element can't be placed. An IllegalArgumentException will be thrown if the element == null
		field.putElement(element);
		
	}
	
	//GET methods
	/**
	 * Returns a Set of all Element objects positioned on the board.
	 * @return Returns the Set of all elements on the board.
	 * 			|let
	 * 			|	Set<Element> elements = new HashSet<Element>()
	 * 			|	while(getFieldsInUseIterator().hasNext())
	 * 			|		elements.addAll(iteratorOfFields.next().getElements())
	 * 			|in
	 * 			|	result == elements
	 */
	@Raw
	public Set<Element> getElements() {
		Set<Element> elements = new HashSet<Element>();
		Iterator<Field> iteratorOfFields = getFieldsInUseIterator();
		while(iteratorOfFields.hasNext()) { //iterate over all the field objects of this board
			elements.addAll(iteratorOfFields.next().getElements());
		}
		return elements;
	}
	
	/**
	 * Returns a Set of Robot objects which are linked with a field in use for this board
	 * @return Returns the Set of robots on the board
	 * 			|let
	 * 			|	Set<Robot> robots = new HashSet<Robot>()
	 * 			|	while(getFieldsInUseIterator().hasNext())
	 * 			|		Field currentField = iteratorOfFields.next()
	 * 			|		if(currentField.getRobot() != null)
	 * 			|			robots.add(currentField.getRobot())
	 * 			|in
	 * 			|	result == robots
	 */
	@Raw
	public Set<Robot> getRobots() {
		Set<Robot> robots = new HashSet<Robot>(); //create the set which we will fill with the board's robots and then return
		Iterator<Field> iteratorOfFields = getFieldsInUseIterator();
		while(iteratorOfFields.hasNext()) { //iterate over all the field objects of this board
			Field currentField = iteratorOfFields.next();
			if(currentField.getRobot() != null) //if a robot is placed on this field, add it to the set
				robots.add(currentField.getRobot());
		}
		return robots;
	}
	
	/**
	 * Returns a Set of Wall objects which are linked with a field in use for this board
	 * @return Returns the Set of walls on the board
	 * 			|let
	 * 			|	Set<Wall> walls = new HashSet<Wall>()
	 * 			|	while(getFieldsInUseIterator().hasNext())
	 * 			|		Field currentField = iteratorOfFields.next()
	 * 			|		if(currentField.getWall() != null)
	 * 			|			walls.add(currentField.getWall())
	 * 			|in
	 * 			|	result == walls
	 */
	@Raw
	public Set<Wall> getWalls() {
		//same procedure as getRobots()
		Set<Wall> walls = new HashSet<Wall>();
		Iterator<Field> iteratorOfFields = getFieldsInUseIterator();
		while(iteratorOfFields.hasNext()) {
			Field currentField = iteratorOfFields.next();
			if(currentField.getWall() != null)
				walls.add(currentField.getWall());
		}
		return walls;
	}
	
	/**
	 * Returns a Set of Item objects which are linked with a field in use for this board
	 * @return Returns the Set of items on the board
	 * 			|let
	 * 			|	Set<Item> items = new HashSet<Item>()
	 * 			|	while(getFieldsInUseIterator().hasNext())
	 * 			|		items.addAll(iteratorOfFields.next().getItems());
	 * 			|in
	 * 			|	result == items
	 */
	@Raw
	public Set<Item> getItems() {
		Set<Item> items = new HashSet<Item>();
		Iterator<Field> iteratorOfFields = getFieldsInUseIterator();
		while(iteratorOfFields.hasNext())
			items.addAll(iteratorOfFields.next().getItems());
		return items;
	}
	
	/**
	 * Returns a Set of Battery objects which are linked with a field in use for this board
	 * @return Returns the Set of batteries on the board
	 * 			|let
	 * 			|	Set<Battery> batteries = new HashSet<Battery>()
	 * 			|	while(getFieldsInUseIterator().hasNext())
	 * 			|		batteries.addAll(getFieldsInUseIterator().next().getBatteries())
	 * 			|in
	 * 			|	result == batteries
	 */
	@Raw
	public Set<Battery> getBatteries() {
		Set<Battery> batteries = new HashSet<Battery>();
		Iterator<Field> iteratorOfFields = getFieldsInUseIterator();
		while(iteratorOfFields.hasNext())
			batteries.addAll(iteratorOfFields.next().getBatteries());
		return batteries;
	}
	/**
	 * Returns a Set of RepairKit objects which are linked with a field in use for this board
	 * @return Returns the Set of repairKits on the board
	 * 			|let
	 * 			|	Set<RepairKit> repairKits = new HashSet<RepairKit>()
	 * 			|	while(getFieldsInUseIterator().hasNext())
	 * 			|		repairKits.addAll(getFieldsInUseIterator().next().getRepairKits())
	 * 			|in
	 * 			|	result == repairKits
	 */
	@Raw
	public Set<RepairKit> getRepairKits() {
		Set<RepairKit> repairKits = new HashSet<RepairKit>();
		Iterator<Field> iteratorOfFields = getFieldsInUseIterator();
		while(iteratorOfFields.hasNext())
			repairKits.addAll(iteratorOfFields.next().getRepairKits());
		return repairKits;
	}
	/**
	 * Returns a Set of SurpriseBox objects which are linked with a field in use for this board
	 * @return Returns the Set of surpriseBoxes on the board
	 * 			|let
	 * 			|	Set<SurpriseBox> surpriseBoxes = new HashSet<SurpriseBox>()
	 * 			|	while(getFieldsInUseIterator().hasNext())
	 * 			|		surpriseBoxes.addAll(getFieldsInUseIterator().next().getSurpriseBoxes())
	 * 			|in
	 * 			|	result == surpriseBoxes
	 */
	@Raw
	public Set<SurpriseBox> getSurpriseBoxes() {
		Set<SurpriseBox> surpriseBoxes = new HashSet<SurpriseBox>();
		Iterator<Field> iteratorOfFields = getFieldsInUseIterator();
		while(iteratorOfFields.hasNext())
			surpriseBoxes.addAll(iteratorOfFields.next().getSurpriseBoxes());
		return surpriseBoxes;
	}
	
	/**
	 * Returns the first field in the given direction (with respect to the given field) that contains an element.
	 * If there is no field with an element in the given direction, null will be returned.
	 * @param 	position
	 * @param 	orientation
	 * @return Returns the first field in the given direction (with respect to the given field) that contains an element, if no such element exists, null is returned.
	 * 			|let
	 * 			|	List<Field> fieldsInDir = new ArrayList<Field>()
	 * 			|	Iterator<Field> iteratorOfFields = getFieldsInUseIterator()
	 * 			|	while(iteratorOfFields.hasNext())
	 * 			|		Field currentField = iteratorOfFields.next()
	 * 			|		if(!currentField.getElements().isEmpty() && field.isOtherInDirection(currentField, orientation)) then
	 * 			|			fieldsInDir.add(currentField)
	 * 			|in
	 * 			|	result == fieldsInDir
	 * @return	|if(result != null) then 
	 * 			|	result.hasElement == true
	 * @return	|if(result != null) then
	 * 			|	Orientation.positionVectorToOrientation( field.getPosition().getDifferenceVector(result.getPosition) ) == orientation
	 */
	public Field getFirstFieldWithElementInDirection(Field field, Orientation orientation){
		List<Field> fieldsInDir = new ArrayList<Field>(); //in this list we will keep all the fields in the desired direction that have at least one element on it.
		
		Iterator<Field> iteratorOfFields = getFieldsInUseIterator();
		while(iteratorOfFields.hasNext()){
			Field currentField = iteratorOfFields.next();
			if(!currentField.getElements().isEmpty() && field.isOtherInDirection(currentField, orientation)) //if the field has an element on it and is in the desired direction respective to this field
				fieldsInDir.add(currentField); //we add it to the list of field in this direction with an element on it.
		}
		//now we have acquired a list of field object in the direction of 'orientation' with at least one element on it. If the list is empty, return null
		if(fieldsInDir.isEmpty()) return null;
		//else we will return the field in the list the closest to the given field.
		return field.getNearestField(fieldsInDir);
	}
	/**
	 * Returns a list of field objects that are in use and are adjacent to the field which is given as a parameter.
	 * Adjacent fields are situated one up, down, left, right from the field, as well as one diagonal field away from the field.
	 * @param field
	 * 		  The field object of which we want to determine the neighbouring fields that are in use
	 * @return Returns a list of adjacent fields that are in use.
	 * 			|let
	 * 			|	List<Field> neighbourFields = new ArrayList<Field>()
	 * 			|	Iterator<Field> iteratorOfFields = getFieldsInUseIterator()
	 * 			|	while(iteratorOfFields.hasNext())
	 * 			|		Field currentField = iteratorOfFields.next()
	 * 			|		Position diff = field.getPosition().getDifferenceVector(currentField.getPosition())
	 * 			|		if( Math.abs(diff.getX()) <= 1 && Math.abs(diff.getY()) <= 1 ) then
	 * 			|			neighbourFields.add(currentField)
	 * 			|in
	 * 			|	result == neighbourFields
	 */
	public List<Field> getNeighbourFields(Field field){
		List<Field> neighbourFields = new ArrayList<Field>(); //in this list we will keep all the fields that are located exactly one field next to the given field. (as well the diagonal fields)
		Iterator<Field> iteratorOfFields = getFieldsInUseIterator();
		while(iteratorOfFields.hasNext()){
			Field currentField = iteratorOfFields.next();
			Position diff = field.getPosition().getDifferenceVector(currentField.getPosition());
			if( Math.abs(diff.getX()) <= 1 && Math.abs(diff.getY()) <= 1 )
				neighbourFields.add(currentField);
		}
		return neighbourFields;
	}
	/**
	 * Returns a set of Item objects that are located on fields adjacent to the field that is given as a parameter.
	 * 
	 * @param field
	 * 		 The field for which we want to retrieve the items that are located on neighbouring fields
	 * @return If the field that is given has no neighbouring fields that are in use, null is returned
	 * 		   | if(getNeighbourFields(field) == null) return null
	 * @return A set of Item objects is returned
	 * 			|let
	 * 			|	Set<Item> items = new HashSet<Item>()
	 *			|	List<Field> neighbourFields = getNeighbourFields(field)
	 *			|	for(Field currentField: neighbourFields)
	 *			|		items.addAll(currentField.getItems())
	 *			|in
	 *			|	result == items
	 */
	public Set<Item> getItemsOnNeighbourFields(Field field){
		Set<Item> items = new HashSet<Item>();
		List<Field> neighbourFields = getNeighbourFields(field);
		for(Field currentField: neighbourFields)
			items.addAll(currentField.getItems());
		return items;
	}
	/**
	 * Returns an iterator of a set of elements filtered by the given ElementFilter.
	 * @param 	elementFilter
	 * @return |elementFilter.filterElements(this.getElements()).iterator()
	 * @throws IllegalArgumentException
	 * 			|elementFilter == null
	 */
	public Iterator<Element> getElementIteratorByFilter(ElementFilter elementFilter) throws IllegalArgumentException {
		if(elementFilter == null) throw new IllegalArgumentException("elementFilter == null");
		return elementFilter.filterElements(this.getElements()).iterator();
	}
	/**
	 * A method that returns the adjacent field of the field which is given as a parameter,
	 * situated in the orientation which is given as a parameter. If this is not a valid position
	 * for this Board null is returned. If the adjacent field does not exists, it is created.
	 * @param field
	 * 			The field of which we want to retrieve on neighbouring field
	 * @param orientation
	 * 			The orientation in which this neighbouring field should be situated with respect to the given field.
	 * @return If the asked next field is not valid, null is returned
	 * 			| if(!this.isValidPosition( new Position( field.getPosition().getX() + orientation.getDx(), field.getPosition().getY() + orientation.getDy()) )) then
	 * 			| 	result == null
	 * @return If the asked field is valid and existing, the existing field is returned
	 * 			| if(getFieldsInUse().containsKey(nextFieldPos)) then
	 * 			| 	result == getFieldsInUse().get(nextFieldPos)
	 * @return If the asked field is valid but non-existing, it is created and returned
	 * 			| if(!getFieldsInUse().containsKey(nextFieldPos)) then
	 * 			| 	result == this.createField(nextFieldPos)
	 */
	public Field getNextFieldIntoDirection(Field field, Orientation orientation){
		Position nextFieldPos = new Position( field.getPosition().getX() + orientation.getDx(), field.getPosition().getY() + orientation.getDy());
		if(!this.isValidPosition(nextFieldPos)) return null;
		if(getFieldsInUse().containsKey(nextFieldPos)) 
			return getFieldsInUse().get(nextFieldPos);
		return this.createField(nextFieldPos);
	}
}
